﻿//  Copyright © Decebal Mihailescu 2013-2014
//  All rights reserved. This source code and binaries are distributed under 
//  Microsoft Public PatternLicense (Ms-PL)
//  The latest code is available at http://VSDesignPatterns.codeplex.com

//  Redistributions of source code must retain the above copyright notice, 
//  this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, 
//  this list of conditions and the following disclaimer in the documentation 
//  and/or other materials provided with the distribution. 

//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Diagnostics;
using System.Globalization;
using System.IO;

namespace DesignPatternsWizard
{
    using Common;
    public partial class ConfigForm : Form
    {
        private ConfigForm()
        {
            InitializeComponent();
        }
        int _startingTab;
        internal bool _providersChanged;
#if DEBUG
        IWizardWindow _parentWizardWnd;
#else
        readonly IWizardWindow _parentWizardWnd;
#endif
        public ConfigForm(int startingTab)
            : this()
        {
            _startingTab = startingTab;
            if (MEFController.ProjectInfo != null)
                _parentWizardWnd = MEFController.ProjectInfo.GetAsService<IWizardWindow>();
        }

        private void _dataGridViewProviders_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {

            switch (_dataGridViewProviders.Columns[e.ColumnIndex].Name)
            {
                case "InUse":
                    DataGridViewRow dgvr = _dataGridViewProviders.Rows[e.RowIndex];
                    if (dgvr == null)
                        return;
                    string providerName = dgvr.Cells["Provider_Name"].Value.ToString();
                    object val = dgvr.Cells[e.ColumnIndex].Value;
                    if (val != null)
                    {
                        bool bInUse = (bool)val;
                        ILangProviderRegistration ireg = dgvr.Cells["HiddenRegistration"].Value as ILangProviderRegistration;
                        if (ireg != null)
                        {
                            ireg.InUse = !bInUse;
                            LoadProviders();
                            _providersChanged = true;
                        }
                    }
                    break;
                default:
                    return;

            }

        }
        float _assemblyVer;
        bool LPFilter(ILanguageProviderMetaData ilpm)
        {
            if (_parentWizardWnd == null)
                return true;
            else
            {
                IProjectInfo ipi = _parentWizardWnd.GetAsService<IProjectInfo>();
                return (ipi.CodeLang & (ilpm.SupportedLangs | ilpm.CompatibleLangs)) != default(CodeLangEnum);
            }
        }
        private void ConfigForm_Load(object sender, EventArgs e)
        {
            try
            {
                this.CenterToParent();
                LocationsEnum persistedLocation = WizardUtils.Locations;
                LoadEnumInCheckedListBox(persistedLocation);
                ColumnsEnum persistedColumns = WizardUtils.Columns;
                LoadEnumInCheckedListBox(persistedColumns);
                Version v = MEFController.Instance.DPEntryAssembly.GetName().Version;
                _assemblyVer = float.Parse(string.Format("{0}.{1}", v.Major, v.Minor), System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
                this.Text += string.Format(" Assembly Version:{0}", _assemblyVer.ToString(CultureInfo.InvariantCulture));

                string[] defMefStr = Utils.GetMetaDataProviders(LPFilter).Select(ilpm => ilpm.ProviderName).ToArray();
                string[] regProviders = Utils.LangProviderRegistration.GetRegisteredLPNames();
                //does regProviders include defMefStr 
                if (regProviders == null || defMefStr.Except(regProviders, StringComparer.InvariantCultureIgnoreCase).Any())
                {

                    foreach (var lp in MEFController.Instance.GetLPInstatntiated(LPFilter))
                    {
                        _providersChanged |= lp.Value.RegisterLP(true, null, null, this);

                    }

                    if (_providersChanged)
                        ProjectInfo.RefreshLangProviders();

                }
                if (_startingTab != 3)
                {
                    _lnkLblMoreProviders.Links.Clear();
                    _lnkLblMoreProviders.Links.Add(32, 8,
                        "\"http://vsdesignpatterns.codeplex.com/\"");
                    LoadProviders();
                }
                LoadOnlineMenus();
                int searchDest = Utils.OnlineSearchDestination;
                if (searchDest == Convert.ToInt32(_rbVS.Tag))
                    _rbVS.Checked = true;
                else if (searchDest == Convert.ToInt32(_rbBrowser.Tag))
                    _rbBrowser.Checked = true;
                else
                    _rbNowhere.Checked = true;

                int resDest = Utils.OnlineResourcesDestination;
                if (resDest == Convert.ToInt32(_rbResVS.Tag))
                    _rbResVS.Checked = true;
                else if (resDest == Convert.ToInt32(_rbResBrowser.Tag))
                    _rbResBrowser.Checked = true;
                else
                    _rbResNowhere.Checked = true;
                groupBox1.Enabled = _dataGridViewOnlineMenus.Enabled = _ckbxShowSearchMenus.Checked = Utils.ShowSearchMenus;
                _ckbxAddDescription.Checked = Utils.AddPatternDescrition;
                _ckBoxStartExpanded.Enabled = _ckBoxCollapsable.Checked = Utils.CollapseHeaders;
                _ckBoxSortPatternsOnLoad.Checked = Utils.SortPatternsOnLoad;
                _ckBoxHideOnlineLink.Checked = Utils.HideOnlineLink;
                _ckBoxStartExpanded.Checked = Utils.StartExpanded;
                _lblLastVersion.Text = string.Format("Last online version:{0}, current version:{1}", Utils.LastOnlineVer, _assemblyVer);
                _txtDaysToCheck.Text = WizardUtils.DaysToCheckOnline.ToString();
                this._tabControl.SelectTab(_startingTab);
                switch (_startingTab)
                {
                    case 1:
                        //_tabControl.TabPages.Remove(_tabPageColumns);
                        this.Text = "Command locations in Visual Studio";
                        _buttonOK.Enabled = true;
                        _buttonCancel.Enabled = false;
                        break;
                    case 2:
                        _tabControl.TabPages.Remove(_tabPageColumns);
                        this.Text = "Language providers available";
                        _buttonOK.Enabled = true;
                        break;
                    case 3:
                        _tabControl.TabPages.Remove(_tabPageProviders);
                        _tabControl.TabPages.Remove(_tabPageColumns);
                        this.Text = "Configure Online Search Menus";
                        _buttonOK.Enabled = true;
                        break;
                    case 4:
                        _tabControl.TabPages.Remove(_tabPageProviders);
                        _tabControl.TabPages.Remove(_tabPageColumns);
                        _tabControl.TabPages.Remove(_tabPageOnlineSearch);
                        _tabControl.TabPages.Remove(_tabPagePositions);
                        this.Text = "Preferences";
                        _buttonOK.Enabled = true;
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {

                MessageBox.Show(this, "Exception thrown in ConfigForm_Load: " + ex.Message);

            }
        }

        private void LoadProviders()
        {
            string[] names = Utils.LangProviderRegistration.GetRegisteredLPNames();
/*
#if VS2015
            string[] names = Utils.GetMetaDataProviders(LPFilter).Select(ilpm => ilpm.ProviderName).ToArray();
#else
            string[] names = Utils.LangProviderRegistration.GetRegisteredLPNames();
#endif
*/
            if (names == null)
            {
                return;
            }

            CodeLangEnum lang = default(CodeLangEnum);

            if (this.Owner is IWizardWindow)//_parentWizardWnd == null)
            {
                lang = _parentWizardWnd.GetAsService<IProjectInfo>().CodeLang;

            }
            else
            {

                lang = WizardUtils.GetAllFlags<CodeLangEnum>();
                //lang = CodeLangEnum.CSharp | CodeLangEnum.ManagedCpp | CodeLangEnum.NativeCpp | CodeLangEnum.VB;
            }
            var allproviders = names.Select(p =>
            {
                ILangProviderRegistration ireg = Utils.LangProviderRegistration.GetLanguageProviderRegistration(p);
                //TO DO: multiple providers with the same name can be loaded by mistake and 'SingleOrDefault' will throw
                //find a way to filter out the 'bad' providers loaded in error, most likely in MefController.InitMef or ProjectInfo.CreateSpecificLanguageProviders
                ILanguageProviderMetaData ilpmd = MEFController.Instance.GetLPInstatntiated(x => x.ProviderName == p).Keys.FirstOrDefault();

                return new
                {
                    Provider_Name = p,
                    Compatible_Languages = (ilpmd == null) ? CodeLangEnum.None : ilpmd.SupportedLangs | ilpmd.CompatibleLangs,
                    License = (ilpmd == null) ? PatternLicense.Free : ilpmd.LicenseType,
                    Description = (ilpmd == null) ? "" : ilpmd.Description,
                    InUse = (ireg == null) ? false : ireg.InUse,
                    HiddenMetadata = ilpmd,
                    HiddenRegistration = ireg,

                    IsLoaded = (ilpmd != null),
                };
            });

            var providers = allproviders.Where(x => x.IsLoaded &&
                (((x.HiddenMetadata.SupportedLangs | x.HiddenMetadata.CompatibleLangs) & lang) != default(CodeLangEnum))).ToArray();//filter registrations unloadable
            if (providers.Length == 0)
            {
                if (MEFController.ProjectInfo != null)
                    MessageBox.Show(this, "No language providers in ConfigForm_Load");
            }
            else
            {
                _dataGridViewProviders.DataSource = providers;
                _dataGridViewProviders.Columns["HiddenMetadata"].Visible = false;
                _dataGridViewProviders.Columns["HiddenRegistration"].Visible = false;
                _dataGridViewProviders.Columns["IsLoaded"].Visible = false;
                _dataGridViewProviders.Columns["License"].Visible = false;
                _dataGridViewProviders.ReadOnly = false;
                IServiceProvider isp = this.Owner as IServiceProvider;
                if (isp == null)
                    return;
                ILanguageProviderMetaData ilpm = isp.GetAsService<ILanguageProviderMetaData>();
                if (ilpm == null)
                    return;
                string provider = ilpm.ProviderName;
                var rows = _dataGridViewProviders.Rows.OfType<DataGridViewRow>();
                DataGridViewRow selRow = rows.SingleOrDefault(r =>
                {
                    if (r.Cells["HiddenMetadata"].Value == null)
                        return false;
                    else
                        return (r.Cells["HiddenMetadata"].Value as ILanguageProviderMetaData).ProviderName == provider;
                });
            }
        }



        private void LoadEnumInCheckedListBox(LocationsEnum persistedLocation)
        {
            _checkedListBoxPositions.Items.Clear();
            Array ar = Enum.GetValues(typeof(LocationsEnum));
            IEnumerable<LocationsEnum> allLocations = ar.AsQueryable().Cast<LocationsEnum>().Skip(1);//skip 'None'
#if VS2015
            //  foreach (LocationsEnum loc in allLocations.Where(l=>l != LocationsEnum.Standard))//Standard not for VS2015
            foreach (LocationsEnum loc in allLocations)
#else
            foreach (LocationsEnum loc in allLocations)
#endif
            {
                _checkedListBoxPositions.Items.Add(loc, (persistedLocation & loc) != LocationsEnum.None);
            }
        }
        private void LoadEnumInCheckedListBox(ColumnsEnum persistedLocation)
        {
            _checkedListBoxColumns.Items.Clear();
            ColumnsEnum[] allLocations = Utils.GetAllEnumValues<ColumnsEnum>();//ar.AsQueryable().Cast<ColumnsEnum>();
            foreach (ColumnsEnum loc in allLocations)
            {
                _checkedListBoxColumns.Items.Add(loc, (persistedLocation & loc) != default(ColumnsEnum));
            }
        }

        bool _bCancelPos;
        private void _buttonOK_Click(object sender, EventArgs e)
        {
            _bCancelPos = false;
            try
            {
                LocationsEnum ckl = _checkedListBoxPositions.CheckedItems.Cast<LocationsEnum>().Aggregate((acc, loc) => loc | acc);
                WizardUtils.Locations = ckl;
                int res;
                if (Int32.TryParse(_txtDaysToCheck.Text, out res))
                    WizardUtils.DaysToCheckOnline = res;
                else
                    WizardUtils.DaysToCheckOnline = 30;
#if !VS2015
                if (_requiresVSRestart)
                {
                    string thisProc = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName);
                    if (string.Compare(thisProc, "MsiExec", true) != 0)//not installing
                    {
                        string vsargs = "/resetaddin DesignPatterns.Connect /Command File.Exit";
                        Logging.Instance.Log(3, "ConfigForm: running: {0} {1}", Utils.VSPath, vsargs);
                        ProcessStartInfo psi = new ProcessStartInfo(Utils.VSPath, vsargs);
                        psi.WindowStyle = ProcessWindowStyle.Hidden;//change to minimized or hidden
                        Process p = Process.Start(psi);
                        MessageBox.Show(this, "Changes to the Command positions or Online search menus" + Environment.NewLine +
                            "will be available only after you restart Visual Studio!"
#if VS2012
                            //some weird thing with refreshing only for VS2012
                        + Environment.NewLine + "Click on the Start menu and select" + Environment.NewLine + "All Programs ->DesignPatterns->VS2012->Reset Visual Studio Ad-in"
                        + Environment.NewLine + "before restarting the IDE!"
#endif
, "Fair Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        //MessageBox.Show(this, "Changes to the Positions will be available only after you restart Visual Studio.To Do that execute the steps below:\r\n" +
                        //        "1.Close Visual Studio\r\n2.Open a Visual Studio command prompt\r\n3.Run \"devenv /resetaddin DesignPatterns.Connect /Command File.Exit\"\r\n" +
                        //        "4.Restart Visual Studio normally", "Fair Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }


                }
#endif
            }
            catch (InvalidOperationException iex)
            {
#if VS2015
                MessageBox.Show(this,
                    "You must leave at least one position available\r\nin order to continue using this VS extension.",
                    iex.Message, MessageBoxButtons.OK, MessageBoxIcon.Stop);
#else
                MessageBox.Show(this,
                    "You must leave at least one position available\r\nin order to continue using this add in.",
                    iex.Message, MessageBoxButtons.OK, MessageBoxIcon.Stop);
#endif
                _bCancelPos = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "Exception thrown", MessageBoxButtons.OK, MessageBoxIcon.Error);
                WizardUtils.Locations = LocationsEnum.ContextProject;
                _bCancelPos = true;
            }


            try
            {
                var cols = _checkedListBoxColumns.CheckedItems.Cast<ColumnsEnum>();
                if (cols.Any())
                    WizardUtils.Columns = cols.Aggregate((acc, loc) => loc | acc);
                else
                    WizardUtils.Columns = default(ColumnsEnum);
            }
            catch
            {
                WizardUtils.Columns = default(ColumnsEnum);
            }


        }
        bool _requiresVSRestart;
        private void _checkedListBoxPositions_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            ListBox lb = sender as ListBox;
            if (lb != null && lb.SelectedItem != null && lb.SelectedItem.ToString().Length > 0)
            {
                _buttonOK.Enabled = true;
                if (sender == _checkedListBoxPositions)
                    _requiresVSRestart = true;
            }
        }

        private void ConfigForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = _bCancelPos;
        }

        private void _buttonRestoreDefault_Click(object sender, EventArgs e)
        {
            string caption = _tabControl.SelectedTab.Text;
            switch (caption)
            {
                case "Command Positions":
                    ColumnsEnum persistedColumns = WizardUtils.DefaultColumns;
                    LoadEnumInCheckedListBox(persistedColumns);
                    _rbResVS.Checked = true;
                    //Utils.OnlineResourcesDestination = 2;
                    break;
                case "Columns to show":
                    LocationsEnum persistedLocation = WizardUtils.DefaultLocations;
                    LoadEnumInCheckedListBox(persistedLocation);
                    break;
                case "Online Search Menus":
                    //remove value "SearchLocations" to go to defaults
                    foreach (var menu in MEFController.Instance.SearchProviders.Select(p => p.Metadata.ProviderName))
                    {
                        try
                        {
                            using (RegistryKey reg = Registry.CurrentUser.OpenSubKey(string.Format("{0}\\{1}", Common.Utils.AppHive, menu), true))
                            {
                                if (reg != null)
                                    reg.DeleteValue("SearchLocations");
                            }
                        }
                        catch
                        {

                        }
                    }

                    LoadOnlineMenus();
                    _rbVS.Checked = true;

                    _ckbxShowSearchMenus.Checked = Utils.ShowSearchMenus = true;
                    break;
                case "Language Providers":
                default:
                    break;
            }
            _requiresVSRestart = true;
            _buttonOK.Enabled = true;
        }
        private void LoadOnlineMenus()
        {
            var menus = MEFController.Instance.SearchProviders.Select(p => new
            {
                SearchProviderName = p.Metadata.ProviderName,
                // URL = p.Metadata.URL,
                CodeWindow = WindowPopupEnum.CodeWindow == (WindowPopupEnum.CodeWindow & p.SearchLocations),
                XAMLEditor = WindowPopupEnum.XAMLEditor == (WindowPopupEnum.XAMLEditor & p.SearchLocations),
                ReferenceItem = WindowPopupEnum.ReferenceItem == (WindowPopupEnum.ReferenceItem & p.SearchLocations),
                ErrorList = WindowPopupEnum.ErrorList == (WindowPopupEnum.ErrorList & p.SearchLocations),
                // SupportedWnds = p.SearchLocations,
                HTML = WindowPopupEnum.HTML == (WindowPopupEnum.HTML & p.SearchLocations),
                HiddenMenuProvider = p,
            }).ToArray();

            _dataGridViewOnlineMenus.DataSource = menus;
            _dataGridViewOnlineMenus.Columns["HiddenMenuProvider"].Visible = false;
            _dataGridViewOnlineMenus.ReadOnly = false;

        }


        private void _dataGridViewOnlineMenus_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            WindowPopupEnum crtWnd = (WindowPopupEnum)Enum.Parse(typeof(WindowPopupEnum), _dataGridViewOnlineMenus.Columns[e.ColumnIndex].Name);
            SearchProvider sp = _dataGridViewOnlineMenus.Rows[e.RowIndex].Cells["HiddenMenuProvider"].Value as SearchProvider;
            sp.SearchLocations ^= crtWnd;
            LoadOnlineMenus();
            _requiresVSRestart = true;
        }

        private void _rb_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            Utils.OnlineSearchDestination = Convert.ToInt32(rb.Tag);
        }

        private void _rbRes_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            Utils.OnlineResourcesDestination = Convert.ToInt32(rb.Tag);
        }

        private void _ckbxShowSearchMenus_CheckedChanged(object sender, EventArgs e)
        {
            if (Utils.ShowSearchMenus != _ckbxShowSearchMenus.Checked)
            {
                _requiresVSRestart = true;
                _dataGridViewOnlineMenus.Enabled = groupBox1.Enabled = _ckbxShowSearchMenus.Checked;
                Utils.ShowSearchMenus = _ckbxShowSearchMenus.Checked;
            }

        }

        private void _ckbxAddDescription_CheckedChanged(object sender, EventArgs e)
        {
            Utils.AddPatternDescrition = _ckbxAddDescription.Checked;
        }

        private void _lnkLblMoreProviders_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process p = new System.Diagnostics.Process();
            p.StartInfo.FileName = (sender as LinkLabel).Links[0].LinkData.ToString();
            p.Start();
        }

        private void _ckBoxCollapsable_CheckedChanged(object sender, EventArgs e)
        {
            Utils.CollapseHeaders = _ckBoxCollapsable.Checked;
            _ckBoxStartExpanded.Enabled = Utils.CollapseHeaders;
        }

        private void _buttonCheck4NewVersion_Click(object sender, EventArgs e)
        {
            try
            {

                this.UseWaitCursor = true;
                Application.DoEvents();
                string downloadUrl;
                float onlineVersion;
                Utils.LastOnlineCheck = DateTime.Today;
                if (Utils.NewOnlineVersionExists(out downloadUrl, out onlineVersion))
                {
                    if (MessageBox.Show(this, "Version " + onlineVersion.ToString(CultureInfo.InvariantCulture) + " is available online.\r\nWould you like to download it?", "A new version is available", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        System.Diagnostics.Process.Start(string.IsNullOrEmpty(downloadUrl) ? Utils.BaseURL : downloadUrl);
                    }
                }
                else
                    if (onlineVersion > _assemblyVer || onlineVersion == 0.0)//not sure
                    System.Diagnostics.Process.Start(Utils.BaseURL);
                else
                    MessageBox.Show(this, "There is no new version available online.\r\nCheck back in a few days.", "No new version is available");
                Version v = MEFController.Instance.DPEntryAssembly.GetName().Version;
                float ver = float.Parse(string.Format("{0}.{1}", v.Major, v.Minor), System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
                _lblLastVersion.Text = string.Format("Last online version:{0}, current version:{1}", onlineVersion, ver);

            }
            catch //(Exception ex)
            {

            }
            finally
            {
                UseWaitCursor = false;
            }
        }

        private void _rbResBrowser_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            Utils.OnlineResourcesDestination = Convert.ToInt32(rb.Tag);
        }

        private void _rbResNowhere_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            Utils.OnlineResourcesDestination = Convert.ToInt32(rb.Tag);
        }

        private void _rbResVS_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton rb = sender as RadioButton;
            Utils.OnlineResourcesDestination = Convert.ToInt32(rb.Tag);
        }

        private void _ckBoxStartExpanded_CheckedChanged(object sender, EventArgs e)
        {
            Utils.StartExpanded = _ckBoxStartExpanded.Checked;
        }

        private void _ckBoxSortPatternsOnLoad_CheckedChanged(object sender, EventArgs e)
        {
            Utils.SortPatternsOnLoad = _ckBoxSortPatternsOnLoad.Checked;

        }

        private void _ckBoxHideOnlineLink_CheckedChanged(object sender, EventArgs e)
        {
            Utils.HideOnlineLink = _ckBoxHideOnlineLink.Checked;
        }
    }
}
